/**
 * 统一日志系统
 * 替代项目中混乱的console.log使用，提供结构化、分级的日志记录
 */

import { ErrorLevel } from '../error/ErrorTypes';

/**
 * 日志级别枚举
 */
export enum LogLevel {
    TRACE = 0,
    DEBUG = 1,
    INFO = 2,
    WARN = 3,
    ERROR = 4,
    FATAL = 5
}

/**
 * 日志上下文信息
 */
export interface LogContext {
    /** 服务名称 */
    service?: string;
    /** 模块名称 */
    module?: string;
    /** 方法名称 */
    method?: string;
    /** 用户ID */
    userId?: string;
    /** 请求ID */
    requestId?: string;
    /** 会话ID */
    sessionId?: string;
    /** 附加元数据 */
    metadata?: Record<string, any>;
    /** 性能数据 */
    performance?: {
        startTime?: number;
        duration?: number;
        memoryUsage?: number;
    };
}

/**
 * 日志条目接口
 */
export interface LogEntry {
    /** 时间戳 */
    timestamp: string;
    /** 日志级别 */
    level: LogLevel;
    /** 日志消息 */
    message: string;
    /** 上下文信息 */
    context: LogContext;
    /** 错误对象（如果有） */
    error?: Error;
    /** 额外数据 */
    data?: any;
}

/**
 * 日志输出器接口
 */
export interface LogAppender {
    /** 输出器名称 */
    name: string;
    /** 输出日志条目 */
    append(entry: LogEntry): void;
    /** 清理资源 */
    dispose?(): void;
}

/**
 * 控制台日志输出器
 */
export class ConsoleAppender implements LogAppender {
    name = 'console';

    append(entry: LogEntry): void {
        const timestamp = entry.timestamp;
        const level = LogLevel[entry.level];
        const service = entry.context.service || 'Unknown';
        const message = entry.message;
        
        // 构建日志消息
        const logMessage = `[${timestamp}] [${level}] [${service}] ${message}`;
        
        // 根据级别选择输出方法
        switch (entry.level) {
            case LogLevel.TRACE:
            case LogLevel.DEBUG:
                console.debug(logMessage, this.formatData(entry));
                break;
            case LogLevel.INFO:
                console.info(logMessage, this.formatData(entry));
                break;
            case LogLevel.WARN:
                console.warn(logMessage, this.formatData(entry));
                break;
            case LogLevel.ERROR:
            case LogLevel.FATAL:
                console.error(logMessage, this.formatData(entry));
                if (entry.error) {
                    console.error('Error details:', entry.error);
                }
                break;
        }
    }

    private formatData(entry: LogEntry): any {
        const data: any = {};
        
        if (entry.context.module) data.module = entry.context.module;
        if (entry.context.method) data.method = entry.context.method;
        if (entry.context.userId) data.userId = entry.context.userId;
        if (entry.context.requestId) data.requestId = entry.context.requestId;
        if (entry.context.metadata) data.metadata = entry.context.metadata;
        if (entry.context.performance) data.performance = entry.context.performance;
        if (entry.data) data.data = entry.data;
        
        return Object.keys(data).length > 0 ? data : undefined;
    }
}

/**
 * 文件日志输出器
 */
export class FileAppender implements LogAppender {
    name = 'file';
    private logQueue: LogEntry[] = [];
    private isWriting = false;
    private maxQueueSize = 1000;

    constructor(
        private filename: string = 'game.log',
        private maxFileSize: number = 50 * 1024 * 1024 // 50MB
    ) {
        // 定期刷新日志到文件
        setInterval(() => this.flushLogs(), 5000);
    }

    append(entry: LogEntry): void {
        this.logQueue.push(entry);
        
        // 防止队列过长
        if (this.logQueue.length > this.maxQueueSize) {
            this.logQueue.shift();
        }
        
        // 如果是错误级别，立即刷新
        if (entry.level >= LogLevel.ERROR) {
            this.flushLogs();
        }
    }

    private async flushLogs(): Promise<void> {
        if (this.isWriting || this.logQueue.length === 0) {
            return;
        }

        this.isWriting = true;
        
        try {
            const fs = require('fs').promises;
            const path = require('path');
            
            // 确保日志目录存在
            const logDir = path.dirname(this.filename);
            await fs.mkdir(logDir, { recursive: true });
            
            // 检查文件大小，如果太大则轮转
            try {
                const stats = await fs.stat(this.filename);
                if (stats.size > this.maxFileSize) {
                    await this.rotateLogFile();
                }
            } catch (error) {
                // 文件不存在，忽略错误
            }
            
            // 格式化日志条目
            const logs = this.logQueue.splice(0).map(entry => this.formatLogEntry(entry));
            const logContent = logs.join('\n') + '\n';
            
            // 追加到文件
            await fs.appendFile(this.filename, logContent, 'utf8');
            
        } catch (error) {
            console.error('Failed to write logs to file:', error);
        } finally {
            this.isWriting = false;
        }
    }

    private formatLogEntry(entry: LogEntry): string {
        const logData = {
            timestamp: entry.timestamp,
            level: LogLevel[entry.level],
            message: entry.message,
            context: entry.context,
            data: entry.data,
            error: entry.error ? {
                name: entry.error.name,
                message: entry.error.message,
                stack: entry.error.stack
            } : undefined
        };
        
        return JSON.stringify(logData);
    }

    private async rotateLogFile(): Promise<void> {
        const fs = require('fs').promises;
        const path = require('path');
        
        const ext = path.extname(this.filename);
        const base = path.basename(this.filename, ext);
        const dir = path.dirname(this.filename);
        
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const rotatedName = path.join(dir, `${base}-${timestamp}${ext}`);
        
        try {
            await fs.rename(this.filename, rotatedName);
        } catch (error) {
            console.error('Failed to rotate log file:', error);
        }
    }

    dispose(): void {
        this.flushLogs();
    }
}

/**
 * 统一日志记录器
 */
export class UnifiedLogger {
    private static instance: UnifiedLogger;
    private appenders: LogAppender[] = [];
    private currentLevel: LogLevel = LogLevel.INFO;
    private defaultContext: LogContext = {};

    private constructor() {
        // 默认添加控制台输出器
        this.addAppender(new ConsoleAppender());
        
        // 根据环境添加文件输出器
        if (process.env.NODE_ENV === 'production') {
            this.addAppender(new FileAppender('logs/game.log'));
        }
    }

    /**
     * 获取单例实例
     */
    static getInstance(): UnifiedLogger {
        if (!this.instance) {
            this.instance = new UnifiedLogger();
        }
        return this.instance;
    }

    /**
     * 设置日志级别
     */
    setLevel(level: LogLevel): void {
        this.currentLevel = level;
    }

    /**
     * 设置默认上下文
     */
    setDefaultContext(context: LogContext): void {
        this.defaultContext = { ...this.defaultContext, ...context };
    }

    /**
     * 添加日志输出器
     */
    addAppender(appender: LogAppender): void {
        this.appenders.push(appender);
    }

    /**
     * 移除日志输出器
     */
    removeAppender(name: string): void {
        const index = this.appenders.findIndex(a => a.name === name);
        if (index >= 0) {
            const appender = this.appenders.splice(index, 1)[0];
            if (appender.dispose) {
                appender.dispose();
            }
        }
    }

    /**
     * 记录日志
     */
    log(level: LogLevel, message: string, context?: LogContext, data?: any, error?: Error): void {
        if (level < this.currentLevel) {
            return;
        }

        const entry: LogEntry = {
            timestamp: new Date().toISOString(),
            level,
            message,
            context: { ...this.defaultContext, ...context },
            data,
            error
        };

        // 输出到所有输出器
        this.appenders.forEach(appender => {
            try {
                appender.append(entry);
            } catch (error) {
                // 避免日志输出本身出错
                console.error('Log appender error:', error);
            }
        });
    }

    /**
     * TRACE级别日志
     */
    trace(message: string, context?: LogContext, data?: any): void {
        this.log(LogLevel.TRACE, message, context, data);
    }

    /**
     * DEBUG级别日志
     */
    debug(message: string, context?: LogContext, data?: any): void {
        this.log(LogLevel.DEBUG, message, context, data);
    }

    /**
     * INFO级别日志
     */
    info(message: string, context?: LogContext, data?: any): void {
        this.log(LogLevel.INFO, message, context, data);
    }

    /**
     * WARN级别日志
     */
    warn(message: string, context?: LogContext, data?: any): void {
        this.log(LogLevel.WARN, message, context, data);
    }

    /**
     * ERROR级别日志
     */
    error(message: string, context?: LogContext, data?: any, error?: Error): void {
        this.log(LogLevel.ERROR, message, context, data, error);
    }

    /**
     * FATAL级别日志
     */
    fatal(message: string, context?: LogContext, data?: any, error?: Error): void {
        this.log(LogLevel.FATAL, message, context, data, error);
    }

    /**
     * 性能监控日志
     */
    performance(message: string, startTime: number, context?: LogContext, data?: any): void {
        const duration = Date.now() - startTime;
        const memoryUsage = process.memoryUsage().heapUsed;
        
        this.info(message, {
            ...context,
            performance: {
                startTime,
                duration,
                memoryUsage
            }
        }, data);
    }

    /**
     * 清理所有输出器
     */
    dispose(): void {
        this.appenders.forEach(appender => {
            if (appender.dispose) {
                appender.dispose();
            }
        });
        this.appenders = [];
    }
}

/**
 * 创建特定服务的日志记录器
 */
export class ServiceLogger {
    private logger: UnifiedLogger;
    private serviceContext: LogContext;

    constructor(serviceName: string, module?: string) {
        this.logger = UnifiedLogger.getInstance();
        this.serviceContext = {
            service: serviceName,
            module
        };
    }

    /**
     * 创建带有方法上下文的日志记录器
     */
    forMethod(methodName: string): MethodLogger {
        return new MethodLogger(this.logger, {
            ...this.serviceContext,
            method: methodName
        });
    }

    trace(message: string, context?: LogContext, data?: any): void {
        this.logger.trace(message, { ...this.serviceContext, ...context }, data);
    }

    debug(message: string, context?: LogContext, data?: any): void {
        this.logger.debug(message, { ...this.serviceContext, ...context }, data);
    }

    info(message: string, context?: LogContext, data?: any): void {
        this.logger.info(message, { ...this.serviceContext, ...context }, data);
    }

    warn(message: string, context?: LogContext, data?: any): void {
        this.logger.warn(message, { ...this.serviceContext, ...context }, data);
    }

    error(message: string, context?: LogContext, data?: any, error?: Error): void {
        this.logger.error(message, { ...this.serviceContext, ...context }, data, error);
    }

    fatal(message: string, context?: LogContext, data?: any, error?: Error): void {
        this.logger.fatal(message, { ...this.serviceContext, ...context }, data, error);
    }

    performance(message: string, startTime: number, context?: LogContext, data?: any): void {
        this.logger.performance(message, startTime, { ...this.serviceContext, ...context }, data);
    }
}

/**
 * 方法级别的日志记录器
 */
export class MethodLogger {
    constructor(
        private logger: UnifiedLogger,
        private methodContext: LogContext
    ) {}

    trace(message: string, context?: LogContext, data?: any): void {
        this.logger.trace(message, { ...this.methodContext, ...context }, data);
    }

    debug(message: string, context?: LogContext, data?: any): void {
        this.logger.debug(message, { ...this.methodContext, ...context }, data);
    }

    info(message: string, context?: LogContext, data?: any): void {
        this.logger.info(message, { ...this.methodContext, ...context }, data);
    }

    warn(message: string, context?: LogContext, data?: any): void {
        this.logger.warn(message, { ...this.methodContext, ...context }, data);
    }

    error(message: string, context?: LogContext, data?: any, error?: Error): void {
        this.logger.error(message, { ...this.methodContext, ...context }, data, error);
    }

    fatal(message: string, context?: LogContext, data?: any, error?: Error): void {
        this.logger.fatal(message, { ...this.methodContext, ...context }, data, error);
    }

    performance(message: string, startTime: number, context?: LogContext, data?: any): void {
        this.logger.performance(message, startTime, { ...this.methodContext, ...context }, data);
    }

    /**
     * 记录方法开始
     */
    methodStart(parameters?: any): number {
        const startTime = Date.now();
        this.debug('Method started', undefined, { parameters });
        return startTime;
    }

    /**
     * 记录方法结束
     */
    methodEnd(startTime: number, result?: any): void {
        this.performance('Method completed', startTime, undefined, { result });
    }

    /**
     * 记录方法错误
     */
    methodError(startTime: number, error: Error, parameters?: any): void {
        this.error('Method failed', {
            performance: {
                startTime,
                duration: Date.now() - startTime
            }
        }, { parameters }, error);
    }
}

/**
 * 全局日志记录器实例
 */
export const logger = UnifiedLogger.getInstance();

/**
 * 便捷的服务日志记录器创建函数
 */
export function createServiceLogger(serviceName: string, module?: string): ServiceLogger {
    return new ServiceLogger(serviceName, module);
}

/**
 * 日志装饰器 - 自动记录方法调用
 */
export function LogMethod(serviceName?: string) {
    return function(target: any, propertyName: string, descriptor: PropertyDescriptor) {
        const method = descriptor.value;
        const className = target.constructor.name;
        const logger = createServiceLogger(serviceName || className);
        const methodLogger = logger.forMethod(propertyName);

        descriptor.value = async function(...args: any[]) {
            const startTime = methodLogger.methodStart(args);
            
            try {
                const result = await method.apply(this, args);
                methodLogger.methodEnd(startTime, result);
                return result;
            } catch (error) {
                methodLogger.methodError(startTime, error as Error, args);
                throw error;
            }
        };

        return descriptor;
    };
}
